const Project = require("../models/projects");
const fse = require("fs-extra");
const path = require("path");
const shell = require("shelljs");
const archiver = require("archiver");
const { rimraf } = require("rimraf");

// 系统配置
const Config = {
  paths: {
    projects: path.resolve(__dirname, "../../projects"),
    template: path.resolve(__dirname, "../template"),
    output: path.resolve(__dirname, "../public/output"),
  },
  defaultTemplate: "simple_app",
  commands: {
    install: "pnpm install",
    build: "npm run build",
  },
  fileOps: {
    maxRetries: 3,
    retryDelay: 1000,
    timeout: 30000,
    maxBusyTries: 3,
  },
  build: {
    timeout: 300000, // 5分钟构建超时
    maxBuffer: 10 * 1024 * 1024, // 10MB缓冲区
  },
  templates: {
    1: {
      name: "admin",
      title: "管理后台模板",
    },
    2: {
      name: "simple_app",
      title: "简单应用模板",
    },
  },
};

// 响应工具
const Response = {
  success: (data = null, msg = "success") => ({
    code: 200,
    msg,
    data,
  }),
  error: (msg = "操作失败", code = -1) => ({
    code,
    msg,
  }),
};

// 文件工具类
class FileUtils {
  static getPath(type, name) {
    // 防止路径注入
    const safeName = path.normalize(name).replace(/^(\.\.[\/\\])+/, "");

    switch (type) {
      case "project":
        return path.join(Config.paths.projects, safeName);
      case "template":
        return path.join(Config.paths.template, safeName);
      case "output":
        return path.join(Config.paths.output, `${safeName}.zip`);
      case "dist":
        return path.join(Config.paths.projects, safeName, "dist");
      default:
        throw new Error("无效的路径类型");
    }
  }

  static async safeRemove(dirPath) {
    if (!fse.existsSync(dirPath)) return;

    let retryCount = 0;
    while (retryCount < Config.fileOps.maxRetries) {
      try {
        await this.removeWithTimeout(dirPath);
        return;
      } catch (err) {
        retryCount++;
        console.warn(
          `删除重试 ${retryCount}/${Config.fileOps.maxRetries}:`,
          err.message
        );

        if (retryCount === Config.fileOps.maxRetries) {
          throw new Error(`删除失败: ${err.message}`);
        }
        await new Promise((resolve) =>
          setTimeout(resolve, Config.fileOps.retryDelay)
        );
      }
    }
  }

  static removeWithTimeout(dirPath) {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error("删除操作超时"));
      }, Config.fileOps.timeout);

      rimraf(dirPath, {
        maxRetries: Config.fileOps.maxBusyTries,
        recursive: true,
        force: true,
      })
        .then(() => {
          clearTimeout(timeout);
          resolve();
        })
        .catch((err) => {
          clearTimeout(timeout);
          reject(err);
        });
    });
  }

  /**
   * 复制项目模板
   * @param {string|number} templateId - 模板ID
   * @param {string} code - 项目代码
   * @throws {Error} 当模板不存在或复制失败时抛出错误
   */
  static async copyProject(templateId, code) {
    // 获取模板配置
    const template = Config.templates[templateId];
    if (!template) {
      throw new Error(`无效的模板类型: ${templateId}`);
    }

    const sourcePath = this.getPath("template", template.name);
    const targetPath = this.getPath("project", code);

    // 检查源模板是否存在
    if (!fse.existsSync(sourcePath)) {
      throw new Error(`模板 ${template.title} 不存在`);
    }

    try {
      // 如果目标目录存在，先删除
      await this.safeRemove(targetPath);

      // 复制模板文件
      await fse.copy(sourcePath, targetPath, {
        overwrite: true,
        errorOnExist: false,
        filter: (src) => {
          // 过滤掉不需要的文件
          return (
            !src.includes("node_modules") &&
            !src.includes(".git") &&
            !src.endsWith(".log")
          );
        },
      });

      console.log(`项目模板复制成功: ${template.title} -> ${code}`);
    } catch (err) {
      console.error(`复制模板失败:`, err);
      throw new Error(`复制模板失败: ${err.message}`);
    }
  }

  static async outConfig(code, host) {
    try {
      const projectPath = this.getPath("project", code);
      const configPath = path.join(projectPath, "public/config.js");
      const config = await fse.readFile(configPath, "utf-8");

      // 使用更精确的正则表达式匹配 BASE_URL 模式
      const configObj = config.replace(
        /BASE_URL:\s?['"]?.*['"]?,/,
        `BASE_URL: '${host}',`
      );

      await fse.writeFile(configPath, configObj, "utf-8");
    } catch (error) {
      console.error(`更新项目 ${code} 的配置时出错:`, error);
      throw error; // 记录错误后重新抛出
    }
  }

  static async cleanupTempFiles() {
    try {
      const files = await fse.readdir(Config.paths.output);
      const now = Date.now();
      const oneHour = 60 * 60 * 1000;

      await Promise.all(
        files.map(async (file) => {
          try {
            const filePath = path.join(Config.paths.output, file);
            const stats = await fse.stat(filePath);

            if (now - stats.mtimeMs > oneHour) {
              await this.safeRemove(filePath);
            }
          } catch (err) {
            console.error(`清理文件失败 ${file}:`, err);
          }
        })
      );
    } catch (err) {
      console.error("清理临时文件失败:", err);
    }
  }
}

// 项目构建工具类
class ProjectBuilder {
  static async build(code) {
    const projectPath = FileUtils.getPath("project", code);

    if (!fse.existsSync(projectPath)) {
      throw new Error("项目目录不存在");
    }

    shell.cd(projectPath);
    const options = {
      timeout: Config.build.timeout,
      maxBuffer: Config.build.maxBuffer,
    };

    // 安装依赖
    const installResult = await shell.exec(Config.commands.install, options);
    if (installResult.code !== 0) {
      throw new Error(`依赖安装失败: ${installResult.stderr}`);
    }

    // 构建项目
    const buildResult = await shell.exec(Config.commands.build, options);
    if (buildResult.code !== 0) {
      throw new Error(`项目构建失败: ${buildResult.stderr}`);
    }
  }

  static async package(code) {
    const outputPath = FileUtils.getPath("output", code);
    const distPath = FileUtils.getPath("dist", code);

    if (!fse.existsSync(distPath)) {
      throw new Error("构建目录不存在");
    }

    return new Promise((resolve, reject) => {
      const output = fse.createWriteStream(outputPath);
      const archive = archiver("zip", { zlib: { level: 9 } });

      output.on("close", () => {
        console.log(`项目打包完成: ${archive.pointer()} bytes`);
        resolve(true);
      });

      output.on("error", (err) => {
        console.error("输出流错误:", err);
        reject(new Error("打包失败"));
      });

      archive.on("error", (err) => {
        console.error("压缩错误:", err);
        reject(new Error("压缩失败"));
      });

      archive.on("warning", (err) => {
        if (err.code !== "ENOENT") {
          console.warn("打包警告:", err);
        }
      });

      archive.pipe(output);
      archive.directory(distPath, false);
      archive.finalize();
    });
  }
}

// 构建查询条件
const buildQuery = (filters) => {
  return Object.entries(filters).reduce((query, [key, value]) => {
    if (value && ["name", "code", "type"].includes(key)) {
      query[key] = ["name", "code"].includes(key)
        ? new RegExp(value, "i")
        : value;
    }
    return query;
  }, {});
};

// 项目控制器
class ProjectController {
  constructor() {
    this.initCleanupTask();
  }

  initCleanupTask() {
    const cleanup = async () => {
      try {
        await FileUtils.cleanupTempFiles();
      } catch (err) {
        console.error("清理任务失败:", err);
      }
    };

    cleanup();
    setInterval(cleanup, 60 * 60 * 1000); // 每小时执行一次
  }

  // 获取项目列表
  async get(ctx) {
    try {
      const { limit = 10, page = 1, ...filters } = ctx.query;
      const query = buildQuery(filters);
      const [total, list] = await Promise.all([
        Project.countDocuments(query),
        Project.find(query)
          .sort({ _id: -1 })
          .skip((page - 1) * parseInt(limit))
          .limit(parseInt(limit)),
      ]);

      ctx.body = {
        code: 200,
        msg: "success",
        data: { list, total, page, limit },
      };
    } catch (err) {
      ctx.body = { code: -1, msg: err.message };
    }
  }

  // 获取所有项目
  async all(ctx) {
    try {
      const projects = await Project.find();
      ctx.body = {
        code: 200,
        msg: "success",
        data: projects,
      };
    } catch (err) {
      ctx.body = { code: -1, msg: err.message };
    }
  }

  // 创建项目
  async create(ctx) {
    try {
      const { name, code, apiUrl, template } = ctx.request.body;

      // 参数验证
      if (!name || !code || !template) {
        ctx.body = { code: -1, msg: "缺少必要参数" };
        return;
      }

      // 检查项目是否存在
      const exists = await Project.findOne({
        $or: [{ code }, { name }],
      });

      if (exists) {
        ctx.body = { code: -1, msg: "项目已存在" };
        return;
      }

      // 复制项目文件
      try {
        await FileUtils.copyProject(template, code);
        await FileUtils.outConfig(code, apiUrl);
      } catch (err) {
        ctx.body = { code: -1, msg: err.message };
        return;
      }

      // 创建项目记录
      const project = await new Project({
        name,
        code,
        apiUrl,
        template,
        createAt: new Date(),
        updateAt: new Date(),
      }).save();

      ctx.body = {
        code: 200,
        msg: "success",
        data: project,
      };
    } catch (err) {
      console.error("创建项目失败:", err);
      ctx.body = { code: -1, msg: err.message };
    }
  }

  // 获取项目详情
  async info(ctx) {
    try {
      const { id } = ctx.query;
      const project = await Project.findById(id);

      if (!project) {
        ctx.body = { code: -1, msg: "项目不存在" };
        return;
      }

      ctx.body = {
        code: 200,
        msg: "success",
        data: project,
      };
    } catch (err) {
      ctx.body = { code: -1, msg: err.message };
    }
  }

  // 修改项目
  async modify(ctx) {
    try {
      const { _id, name, code, apiUrl } = ctx.request.body;

      // 检查是否存在同名项目
      const exists = await Project.findOne({
        $or: [{ code }, { name }],
        _id: { $ne: _id },
      });

      if (exists) {
        ctx.body = { code: -1, msg: "项目已存在" };
        return;
      }

      // 获取原项目信息
      const project = await Project.findById(_id);
      if (!project) {
        ctx.body = { code: -1, msg: "项目不存在" };
        return;
      }

      if (project.code !== code) {
        // 重命名项目目录
        const oldPath = FileUtils.getPath("project", project.code);
        const newPath = FileUtils.getPath("project", code);

        // 如果目标目录已存在，先删除
        if (fse.existsSync(newPath)) {
          await FileUtils.safeRemove(newPath);
        }

        await fse.rename(oldPath, newPath);
      }

      // 更新项目信息
      await Project.updateOne(
        { _id },
        {
          name,
          code,
          apiUrl,
          updateAt: new Date(),
        }
      );

      ctx.body = { code: 200, msg: "success" };
    } catch (err) {
      ctx.body = { code: -1, msg: err.message };
    }
  }

  // 打包项目
  async package(ctx) {
    try {
      const { id } = ctx.params;
      const project = await Project.findById(id);

      if (!project) {
        ctx.body = { code: -1, msg: "项目不存在" };
        return;
      }

      // 构建项目
      await ProjectBuilder.build(project.code);

      // 打包项目
      const success = await ProjectBuilder.package(project.code);

      ctx.body = success
        ? {
            code: 200,
            msg: "success",
            data: { url: `/output/${project.code}.zip` },
          }
        : { code: -1, msg: "打包失败" };
    } catch (err) {
      ctx.body = { code: -1, msg: err.message };
    }
  }

  // 删除项目
  async remove(ctx) {
    try {
      const { id } = ctx.params;
      const project = await Project.findById(id);

      if (!project) {
        ctx.body = { code: -1, msg: "项目不存在" };
        return;
      }

      // 先删除文件，再删除数据库记录
      await FileUtils.safeRemove(FileUtils.getPath("project", project.code));

      await Project.findByIdAndDelete(id);
      ctx.body = { code: 200, msg: "success" };
    } catch (err) {
      console.error("删除项目失败:", err);
      ctx.body = { code: -1, msg: err.message };
    }
  }
}

module.exports = new ProjectController();
